﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
using System.Xml.Serialization;

namespace IFWiki.Utils
{
    public class Persister
    {
        public static string BasePath { get; set;}

        
        public static void Save(string name, object obj, DateTime? archiveTime)
        {
            try
            {
                if (archiveTime != null)
                {
                    Save(Path.Combine("Archive", name + "_" + archiveTime.Value.ToString("yyyyMMddHHmmss")), obj, null);
                }

                var fileName = Path.Combine(Path.Combine(BasePath, obj.GetType().ToString().Clean()), name.Clean());
                IOUtils.EnsurePathExists(Path.GetDirectoryName(fileName));

                var xsZing = new XmlSerializer(obj.GetType());
                using (var twWriter = new StreamWriter(fileName))
                {
                    xsZing.Serialize(twWriter, obj);
                }

            }
            catch (Exception )
            {
                //TODO:Log("Error while saving file to object. file:'" + fileName + "'", ex);
                throw;
            }
        }

        public static object Load(string name, Type type)
        {
            return Load(name, type, type.ToString());
        }


        public static object Load(string name, Type type, string subfolder )
        {
            var fileName = Path.Combine(Path.Combine(BasePath, subfolder.Clean()), name.Clean());

            return LoadFile(fileName, type);

        }

        private static object LoadFile(string fileName, Type type)
        {
            if (!File.Exists(fileName))
            {
                return null;
                //throw new FileNotFoundException("File Not Found, hence can't deserialize into " + type.ToString(), xmlFileName);
            }

            System.Xml.Serialization.XmlSerializer xsZing = new System.Xml.Serialization.XmlSerializer(type);

            using (FileStream fsStream = new FileStream(fileName, FileMode.Open))
            using (XmlTextReader xrReader = new XmlTextReader(fsStream))
            {
                try
                {
                    return xsZing.Deserialize(xrReader);
                }
                catch (Exception ex)
                {
                    //Archive the failed file, so that it's not 
                    //permanently over written upon save

                    //TODO:Archive(xmlFileName, "failed");

                    //TODO: Log("Error loading file '" + xmlFileName + "', details:" + ex.ToString());
                    //throw;
                    return null;
                }
            }
        }

        internal static List<T> LoadList<T>(string name, Type innerType)
        {
            return LoadList<T>(name, innerType, innerType.ToString());
        }



        internal static T LoadRevision<T>(string name, DateTime revisedAt, Type innerType)
        {
            return LoadRevision<T>(name, revisedAt, innerType, innerType.ToString());
        }

        internal static T LoadRevision<T>(string name, DateTime revisedAt, Type innerType, string subfolder)
        {
            var fileName = name.Clean() + "_" + revisedAt.ToString("yyyyMMddHHmmss");
            string path = Path.Combine(Path.Combine(BasePath, subfolder.Clean()), "Archive");
            fileName = Path.Combine(path, fileName);
            return (T)LoadFile(fileName, innerType);
        }

        internal static List<T> LoadList<T>(string name, Type innerType, string subfolder )
        {
            var result = new List<T>();
            var searchPattern = name.Clean() + "_" + "??????????????";
            string path = Path.Combine(Path.Combine(BasePath, subfolder.Clean()), "Archive");

            var files = Directory.GetFiles(path, searchPattern);
            foreach (var file in files)
            {
                result.Add((T)LoadFile(file, innerType));
            }

            return result;
        }
    }
}
